Skip to content

fix(tests): unbreak main - disambiguate Count overload after IsEqualTo wrappers (#5751)#5759

Merged
thomhurst merged 2 commits intomainfrom
fix/collection-count-test-overload-ambiguity
Apr 26, 2026
Merged

fix(tests): unbreak main - disambiguate Count overload after IsEqualTo wrappers (#5751)#5759
thomhurst merged 2 commits intomainfrom
fix/collection-count-test-overload-ambiguity

Conversation

@thomhurst
Copy link
Copy Markdown
Owner

Summary

  • Main is currently red on all 3 OS jobs of the .NET workflow with CS1061: 'CollectionCountSource<string[], string>' does not contain a definition for 'And' in TUnit.Assertions.Tests/CollectionAssertionTests.cs:270.
  • Root cause: PR feat(assertions): IsEqualTo with implicitly-convertible wrappers (#5720) #5751 (feat(assertions): IsEqualTo with implicitly-convertible wrappers) added IsEqualTo<TValue, TOther> with an implicit-conversion fallback. That made c.IsEqualTo(3) against IAssertionSource<string> compile, where it previously didn't. The test relied on that earlier compile error to push overload resolution to the inline-count assertion form (Count(Func<IAssertionSource<int>, Assertion<int>?>)). Now resolution picks the per-item filter overload Count(Func<IAssertionSource<TItem>, IAssertion?>) which returns CollectionCountSource<,> — a builder with no .And.
  • Fix: switch the test to the same pattern the int variant (Chained_Collection_Assertions) already uses: .Count().IsEqualTo(3). The result is CollectionCountEqualsAssertion (a CollectionAssertionBase), so .And is available regardless of the item type.

No production code change; behaviour of .Count(itemAssertion) and .IsEqualTo<,> is unchanged.

Test plan

  • dotnet build TUnit.Assertions.Tests -c Release → 0 errors locally
  • dotnet run --framework net10.0 -- --treenode-filter "/*/*/CollectionAssertionTests/Chained_Collection_Assertions_WithStrings*" → 1/1 passed
  • Full .NET workflow on PR turns green (windows / ubuntu / macos)

…5751)

Chained_Collection_Assertions_WithStrings called
.Count(c => c.IsEqualTo(3)) on string[]. Before #5751 this failed to
bind to the per-item filter overload (int-vs-string mismatch on
IsEqualTo) and resolved to the inline-count assertion form, returning
a chainable assertion. After #5751 added IsEqualTo<TValue, TOther>
with implicit-conversion fallback, both overloads compile and
resolution picks the per-item filter, which returns
CollectionCountSource<,> -- a count source without .And -- breaking
.And.Contains("Bob") with CS1061.

Mirror the int test (Chained_Collection_Assertions) and use
.Count().IsEqualTo(3) so the resulting assertion is the chainable
CollectionCountEqualsAssertion regardless of TItem.
Copy link
Copy Markdown
Contributor

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of PR #5759: Fix Chained_Collection_Assertions_WithStrings compilation failure

This is a clean, minimal fix for the regression introduced by PR #5751. The root cause analysis is accurate and the chosen fix is the right one. Here is my assessment:

What the fix does

The test previously relied on an implicit compile error to steer overload resolution toward the inline-count form Count(Func<IAssertionSource<int>, Assertion<int>?>). PR #5751 introduced IsEqualTo<TValue, TOther> at [OverloadResolutionPriority(-1)], which made c.IsEqualTo(3) against IAssertionSource<string> compile (via the implicit-conversion fallback). This changed overload resolution so the compiler now picks Count(Func<IAssertionSource<TItem>, IAssertion?>) — the per-item filter overload — which returns CollectionCountSource<string[], string>, a builder object that has no .And.

The fix switches to the .Count().IsEqualTo(3) form, which routes through CollectionCountSource<TCollection, TItem>.IsEqualTo(int)CollectionCountEqualsAssertion<TCollection, TItem>, a proper CollectionAssertionBase subclass that does expose .And. This is consistent with how Chained_Collection_Assertions (the int-array variant) already works.

Design observations

The fix itself is correct. There are no production-code changes, only a test correction. The 2-line change is the minimal, targeted solution.

The underlying design tension is worth noting (not a blocker for this PR). There are now three Count overloads on CollectionAssertionBase:

Count()                                                   → CollectionCountSource   (numeric builder)
Count(Func<IAssertionSource<int>, Assertion<int>?>)       → CollectionCountWithInlineAssertionAssertion   (priority -1)
Count(Func<IAssertionSource<TItem>, IAssertion?>)         → CollectionCountSource   (per-item filter)

When TItem = int, both predicate overloads are applicable for a lambda like c => c.IsEqualTo(5), and the [OverloadResolutionPriority(-1)] on the inline-count form correctly deprioritises it. But as this PR demonstrates, the interaction between these overloads and the new IsEqualTo<TValue, TOther> implicit-conversion overload can be surprising — the compiler now silently picks the per-item filter form for non-int collections when the lambda happens to compile via the implicit-conversion fallback. A future improvement could be a Roslyn analyzer diagnostic that warns when Count(predicate) is used in a way that returns a CollectionCountSource without a subsequent numeric assertion, flagging the likely intent mismatch.

The updated comment in the test is accurate and helpful. Explaining that Count(predicate) is a per-item filter that returns a builder without .And provides future readers with the context needed to choose the right form.

Correctness

  • The replaced expression .Count().IsEqualTo(3) correctly counts the total number of elements (no predicate → simple ICollection.Count fast-path) and chains back to a CollectionAssertionBase, so .And.Contains(...) and .And.DoesNotContain(...) work as intended.
  • No production code is changed.
  • There is no risk to AOT compatibility or dual-mode (source-gen + reflection) operation.

Verdict

LGTM. The fix is minimal, correct, and consistent with the existing pattern in the sibling test. Merging this unblocks main without any behavioural regression.

@thomhurst thomhurst merged commit 39a9ed5 into main Apr 26, 2026
13 of 14 checks passed
@thomhurst thomhurst deleted the fix/collection-count-test-overload-ambiguity branch April 26, 2026 11:35
@codacy-production
Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant